import java.lang.StringBuilder
import java.util.*

/**
 * 主函数
 * fun 关键字 function(方法,函数),kotlin中所有被fun标记的都是一个方法
 * main 关键字,主方法和Java一样都是以main命名
 * args 参数名,是一个形参
 * Array<String> String类型的数组,在kotlin中数组和集合的概念很模糊
 * 注意,与Java不同,Kotlin中声明变量时,名字在前,类型在后,中间用:(冒号)表示
 * println 这个方法是对Java中System.out.println的包装
 */

fun main(args: Array<String>): Unit {
//    println("Hello,world!$name1")
//    a = false
//    list.add(12)
//    println("1 + 2 = ${1 + 2}")
//    println("a ${if (a >= 10) "大于等于10" else "小于10"}")
//
//    println("a ${
//    if (a < 0)
//        "小于0"
//    else
//        "大于0"
//    }")

//    val person = Person("LC", 12)
//    person.age = 13//赋值就是set方法
//    println(person.age)//取值就是get方法
//    println(person.name)
//    //调用Java中类,与kotlin的调用没有任何区别
//    val personJava = PersonJava("lC", 13)
//    personJava.age = 14//赋值调用的就是set方法
//    println(personJava.age)//取值调用的就是get方法,可以自动识别
//    val rectangle = Rectangle(12, 12)
//    println(rectangle.isSquare)
//    val rectangleJava = RectangleJava(12, 13)
//    println(rectangleJava.isSquare)
//    println(mix(Color.RED, Color.YELLOW))
//    println(mix(Color.YELLOW, Color.RED))
//    play()
//    binary()
//    println(regex('$'))
//    println(mutableList.javaClass)
//    println(list.javaClass)
//    println(set.javaClass)
//    println(map.javaClass)
//    println(string.last())
//    println(numbers)
//    println(list)
//    println(joinToString(list, ".."))
//    joinToString(separator = "|", postfix = "(", prefix = ")", collection = list)

    println("kotlin".lastChar())
    println(list.joinToString())
    println(set.joinToString(separator = "||", postfix = "|||", prefix = "|||"))
}

/**
 * 方法(函数)
 * 有无返回值,在kotlin中,所有方法都有返回值,只是没有返回值的方法他的返回值是(:Unit)可以省略
 * Unit 相当于Java中的void
 * a:Int a 是参数名,:Int是类型 : 有继承的意思 也可以看做这个参数(属性,变量)是个什么类型
 * 整个方法后的:Int 代表当前方法的返回值,当前方法返回值是个Int
 * 在kotlin中基本类型比如int,boolean都是大写开头
 */
//fun max(a: Int, b: Int): Int {
//    return if (a > b) a else b
//}

/**
 * kotlin特性,表达式方法体(函数体)
 * 如果一个方法的方法体是由单个表达式构成,可以用这个表达式作为完整的方法体,并且去掉大括号和return语句
 * 同时方法的返回值:Int 也是可以省略的
 */
fun max(a: Int, b: Int) = if (a > b) a else b

/**
 * 变量:
 * val(value) 不可变引用,在初始化之后不能再次赋值,对应Java中的final修饰符
 * var(variable) 可变引用,这种变量的值可以被改变,赌赢Java中的普通变量
 * :String 可以省略,也就是val和var可以自动识别类型,初始化是什么类型,之后使用或者赋值就必须是什么类型
 * 初始化是一个int,再次赋值时就必须是一个int,不能改变类型,可以改变值
 * 有一个情况比较特殊,使用val声明集合的时候,他的集合中的值是可以被改变的
 */

val str: String = "这是一个常量的String"
var a = 12
//val list = arrayListOf<Any>("12", 12) //相当于new了一个ArrayList

/**
 * 字符串模板(拼接字符串)
 * 使用$符号可以直接在字符串中引用某些变量,甚至可以加{}大括号来进行计算和判断
 * 注意写句式的时候,先写出完整的字符串,再加入${} 这样不会乱
 */
val name1 = "LC"

/**
 * 枚举
 * 在kotlin中少有几个需要写分号的位置
 * 代表换行,是一个方法,而不是一个枚举了
 */
enum class Color(val r: Int, val g: Int, val b: Int) {
    RED(255, 0, 0),
    ORANGE(255, 165, 0),
    YELLOW(255, 255, 0),
    GREEN(0, 255, 0),
    BLUE(0, 0, 255);

    fun rgb() = (r * 256 + g) * 256 + b
}

/**
 * when,分支结构,比switch更简单
 */
fun getColorString(color: Color) = when (color) {
//    Color.YELLOW -> "黄色"
//    Color.RED -> "红色"
//    Color.ORANGE -> "橙色"
//    Color.GREEN -> "绿色"
//    Color.BLUE -> "蓝色"
    //when很灵活,可以在多个分钟选择同一个返回,类似switch中去掉break
    Color.YELLOW, Color.RED, Color.ORANGE -> "暖色"
    Color.BLUE -> "冷色"
    Color.GREEN -> "中性色"
}

val b = 2
//var max1 = when {
//    a > b -> {
//        println(a)
//        a
//    }
//    else -> b
//}

fun max2(a: Int, b: Int): Int {
    if (a > b) {
        println(a)
        return a
    } else {
        return b
    }
}

//在when中可以判断集合,在Java中做不到
fun mix(c1: Color, c2: Color) = when (setOf(c1, c2)) {
    setOf(Color.RED, Color.YELLOW) -> "橙色"
    setOf(Color.YELLOW, Color.BLUE) -> "绿色"
    else -> "不知道是什么颜色"
}

/**
 * 智能转换
 * is 相当于Java中的instanceOf
 */
//val animal = Animal()
//fun anim() {
//    if (animal is Dog) {
//        animal.dig()
//    }
//    dog.dig()
//}

/**
 * 强转 as 相当于Java中的((Dog) animal).dig();
 */
//val animal2 = Animal()
//val dog = animal2 as Dog

/**
 * 循环
 *
 * 区间和数列
 * 1..10 1到10并且包含10 1 until 10 1到10 但是不包含10
 */

val oneToTen = 1..10
val oneToNine = 1 until 10

fun fizzBuzz(i: Int) = when {
    i % 15 == 0 -> "FizzBuzz"
    i % 5 == 0 -> "Buzz"
    i % 3 == 0 -> "Fizz"
    else -> "$i"
}

fun play() {
//    for (i in oneToTen) {
//        println(i)
//    }
    for (i in 1..100) {
        println(fizzBuzz(i))
    }
}

fun binary() {
//    //声明了一个map,kv结构的,使用TreeMap是可以让键排序
//    val binaryReps = TreeMap<Char, String>()
//    //循环字母区间,c代表A到F的所有字母
//    for (c in 'A'..'F') {
//        //把A到F的ASCII码转换为二进制码
//        val binary = Integer.toBinaryString(c.toInt())
//        //根据键C 把值存入map中
//        binaryReps[c] = binary
//        //A = "100010"
//        //B = "100100"
//    }
//
//    //key和value是2个形参,一个代表map中的key,一个代表map中的value
//    for ((key, value) in binaryReps) {
//        println("$key=$value")
//    }

    //withIndex() 相当于给每一个元素多了个index的key,element就是他的value
    val list = arrayListOf("10", "11", "1001")
    for ((index, element) in list.withIndex()) {
        println("$index:$element")
    }
}

fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNumber(c: Char) = c in '0'..'9'

/**
 * 使用in检查集合的成员
 */
fun regex(c: Char) = when {
    isLetter(c) -> "这是个字母"
    isNumber(c) -> "着是个数字"
    else -> "这可能是个符号也可能是个中文"
}

/**
 * String? 代表这个声明的对象可以为空
 * 在kotlin中?很重要,代表一个类型是否可以为空,如果声明一个类型可以为空的话必须要加?
 * 在kotlin中,空和非空的类型是两回事儿,比如String和String?是两种不同的类型
 * 非空对象可以给可空对象赋值,也就是c1可以给c2赋值,但是可空对象不能给非空对象赋值,c2不能直接给c1赋值
 * !!代表非空断言,表示这个值一定不为空
 */
var c1: String = "123"
var c2: String? = "123"
var c3: String? = null
//var c4: String = null

fun c22() {
    c1 = c2!!

    c2 = c1
}


/**
 * 集合
 * 在kotlin中直接listOf就是new出一个集合
 * 常用的其实就是mutableListOf 创建可变集合
 * to 并不是一个特殊的结构,就是一个普通的方法,类似于Java中冒号: 因为kotlin中:被占用了(继承或者声明变量的时候需要使用:)
 * 所以用to来构建键值对
 */


val mutableList = mutableListOf(1, 2, 3)//可变的list
val list = listOf(1, 2, 3)//相当于在Java中new了一个List
val set = setOf(1, 2, 3)//相当于在Java中new了一个Set,set是无序的集合,其中的内容没有顺序也没有index
val map = mapOf(1 to "one", 2 to "two")//相当于new了一个map

val string = listOf("first", "second", "fourteenth")
val numbers = setOf(1, 14, 2)

/**
 * kotlin能让方法更好用
 * 命名参数
 * 如果使用了一个命名参数,则其他的参数都必须使用命名参数,可以指定参数的位置
 * 与Java不同,kotlin可以指定参数的位置,不需要一定按照参数的顺序去写
 *
 * 参数的默认值
 * 默认参数,如果在方法中给了默认参数,则在调用方法的时候可以不再次给赋值
 * 如果在有默认参数的情况下,再给了新的参数,则按照新的参数赋值
 */
fun <T> Collection<T>.joinToString(
//        collection: Collection<T>,//集合的顶层接口,代表这个参数可以是list或者set
        separator: String = ".",//分隔符
        prefix: String = "{",//开始的符号
        postfix: String = "}"//结束的符号
): String {
    val result = StringBuilder(prefix)//首先创建一个可拼接的字符串,默认添加开始符号
    //进行循环把list或者set中的每个元素增加一个下标
    for ((index, element) in this.withIndex()) {
        if (index > 0) {
            result.append(separator)//下标大于0的情况下再String中添加一个分隔符
        }
        result.append(element)//每次循环在分隔符之后添加一个集合中的元素
    }
    result.append(postfix)//循环完毕,在string的末尾添加结束符号
    return result.toString()//返回出这个String
}

/**
 * 扩展方法(函数)
 * 给一个已有的类或者类型添加一个方法,在Java中是不可能办到的,因为String无法继承
 */
//kotlin
//String的本质是一个char的数组,这里[]表示get出其中的某一个index下标的元素
//this.length-1代表最后一个字符
fun String.lastChar(): Char = this[this.length - 1]